﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using ca2simulator.shared;
using System.Windows.Media;
using System.Windows.Controls.Primitives;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Threading;
using System.Windows.Threading;
using System.Threading.Tasks;

namespace ca2simulator.client.view.grid
{
    public class LatticeRenderer : IDisposable
    {
        public delegate void UpdateCompleteHanlder(object sender);
        public event UpdateCompleteHanlder UpdateComplete;
        protected void OnUpdateComplete()
        {
            if (UpdateComplete != null)
                UpdateComplete(this);
        }

        private delegate void DelUpdate();

        private const float DPI = 100;

        private Lattice _Lattice;

        private GridBitmap _GridBitmap;
        private DelUpdate _UpdateDelegate;
        Image _Image;
        bool isImageSet = false;

        public Lattice Lattice { get { return _Lattice; } 
            set 
            {
                lock (_Lattice)
                {
                    _Lattice = value;
                }
            } 
        }

        public LatticeRenderer(Lattice lattice, Image image)
        {
            _Lattice = lattice;
            _GridBitmap = new GridBitmap(Lattice.Rows, Lattice.Columns, 1, 1);
            //image.Source = _GridBitmap.Bitmap;
            _Image = image;
            _UpdateDelegate = Update;
        }
        
        public void InvokeUpdate()
        {
            _GridBitmap.Bitmap.Dispatcher.BeginInvoke(_UpdateDelegate,DispatcherPriority.Render);
        }

        private void Update()
        {
            
            lock (_Lattice)
            {
                _GridBitmap.LockBitmap();
                IntPtr buffStart = _GridBitmap.Bitmap.BackBuffer;
                IntPtr travelingPtr = new IntPtr(buffStart.ToInt64());
                int stride = _GridBitmap.Bitmap.BackBufferStride;
                int rows = Lattice.Rows, cols=Lattice.Columns;
                
                Color cAlive = Colors.Blue;
                Color cDead = Colors.Ivory;
                
                unsafe
                {
                    Parallel.For(0, rows, r =>
                    {
                        Color color;
                        for (int c = 0; c < cols; c++)
                        {
                            if (_Lattice[r, c].CellState.State == 1)
                            {
                                color = cAlive;
                            }
                            else color = cDead;
                            _GridBitmap.SetCell(r, c, color, buffStart, stride);
                        }
                    });
                }
                /*
                unsafe
                {
                    for (int r = 0; r < rows; r++)
                    {
                        for (int c = 0; c < cols; c++)
                        {
                            if (_Lattice[r, c].CellState.State == 1)
                            {
                                color = cAlive;
                            }
                            else color = cDead;
                            _GridBitmap.SetCell(r, c, color, buffStart, stride);
                        }
                    }
                }*/
                 
                _GridBitmap.UnlockBitmap(_Image);
            }
            if (!isImageSet)
            {
                _Image.Source = _GridBitmap.Bitmap;
                isImageSet = true;
            }
            OnUpdateComplete();
        }

        public void Dispose()
        {
            lock (_Lattice)
            {
                _GridBitmap.Dispose();
                _GridBitmap = null;
            }
        }
    }

    public class GridBitmap : IDisposable
    {
        private const float DPI = 100;
        private readonly int Rows, Cols, CellW, CellH;
        private Int32Rect _CellRect;

        private WriteableBitmap _Bitmap;
        public WriteableBitmap Bitmap { get { return _Bitmap; } }

        public GridBitmap(int rows, int col, int cellW, int cellH)
        {
            int bitWidth = rows * cellH;
            int bitHeight = col * cellW;
            Rows = rows; Cols = col;
            CellW = cellW; CellH = cellH;
            _CellRect = new Int32Rect(0, 0, CellW, CellH);
            _Bitmap = new WriteableBitmap(bitWidth, bitHeight, DPI, DPI, PixelFormats.Rgb24, null);
        }

        public void LockBitmap()
        {
            _Bitmap.Lock();
        }

        public void UnlockBitmap(Image image)
        {
            _Bitmap.AddDirtyRect(new Int32Rect(0, 0, _Bitmap.PixelWidth, _Bitmap.PixelHeight));
            _Bitmap.Unlock();
        }

        /// <summary>
        /// Sets a cell to the specified color. 
        /// A call to LockBitmap MUST have been made prior to any calls to this method.
        /// The call to UnlockBitmap MUST be made after done setting cells.
        /// </summary>
        /// <param name="row"></param>
        /// <param name="col"></param>
        /// <param name="color"></param>
        public unsafe void SetCell(int row, int col,  Color color, IntPtr buffStart, int stride)
        {   
            
            int width = CellW, height = CellH;
            byte blue = color.B;
            byte green = color.G;
            byte red = color.R;
            _CellRect.X = col;
            _CellRect.Y = row;

                
            byte* pbuff = (byte*)buffStart.ToPointer();
            pbuff += stride * _CellRect.Y;
            pbuff += _CellRect.X * 3;
            for (int y = 0; y < height; y++)
            {
                int x;
                for (x = 0; x < width; x++)
                {
                    pbuff[0] = blue;
                    pbuff[1] = green;
                    pbuff[2] = red;
                          

                    //move pointer
                    pbuff += 3;
                }
                //Move it back to the beginning of the block
                pbuff -= 3 * (x-1);
                // Move it to the next row
                pbuff += stride;
            }
                
            
            
        }

        public void Dispose()
        {
            _Bitmap = null;
        }
    }
}
